package com.mm.update.util;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
public class SftpClient {

    private String username;
    private String password;
    private String aesKey = "qwerasdfzxcv2021";
    private String ip;
    private String protocol;
    private int port = 22;

    public SftpClient(String ip,String username, String password) {
        this.ip = ip;
        this.username = username;
        this.password = password;
    }

    public SftpClient() {
    }

    // 设置第一次登陆的时候提示，可选值：(ask | yes | no)
    private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";

    /**
     * 创建SFTP连接
     * @return
     * @throws Exception
     */
    public ChannelSftp createSftp() throws Exception {
        JSch jsch = new JSch();
        Session session = createSession(jsch, ip, port);
        session.setPassword(password);
        session.connect(30000);
        log.info("Session connected to {}.", ip);
        ChannelSftp channel = (ChannelSftp)session.openChannel("sftp");
        log.info("channel是否为空:{}",channel==null);
        channel.connect(300000);
        log.info("Channel created to {}.", ip);
        return  channel;
    }


    /**
     * 创建session
     * @param jsch
     * @param host
     * @param port
     * @return
     * @throws Exception
     */
    private Session createSession(JSch jsch, String host, Integer port) throws Exception {
        Session session = null;

        if (port <= 0) {
            session = jsch.getSession(username, host);
        } else {
            session = jsch.getSession(username, host, port);
        }

        if (session == null) {
            throw new Exception(host + " session is null");
        }

        session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, "no");
        return session;
    }

    /**
     * 关闭连接
     * @param sftp
     */
    public void disconnect(ChannelSftp sftp) {
        try {
            if (sftp != null) {
                if (sftp.isConnected()) {
                    sftp.disconnect();
                } else if (sftp.isClosed()) {
                    log.info("sftp is closed already");
                }
                if (null != sftp.getSession()) {
                    sftp.getSession().disconnect();
                }
            }
        } catch (JSchException e) {
           log.error("stfp disconnect error:"+e.getMessage());
        }
    }


    /**
     * 将inputStream上传到指定路径下
     * @param targetPath
     * @param inputStream
     * @return
     * @throws Exception
     */
    public boolean uploadFile(String targetPath, InputStream inputStream,String fileName) throws Exception {
        ChannelSftp sftp = this.createSftp();
        try {
            log.info("uploadFile path {}",targetPath);
            sftp.cd(targetPath);
            sftp.put(inputStream, fileName);
            return true;
        } catch (Exception e) {
            log.error("Upload file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Upload File failure");
        } finally {
            inputStream.close();
            this.disconnect(sftp);
        }
    }

    public String executeCmd(String cmd) throws Exception {
        JSch jsch = new JSch();
        Session session = createSession(jsch, ip, port);
        session.setPassword(password);
        session.connect(30000);
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        StringBuilder sb;
        try {
            channelExec.setCommand(cmd);
            channelExec.setInputStream(null);
            channelExec.setErrStream(System.err);
            log.error("执行命令：{}------------start--------",cmd);
            channelExec.connect();
            InputStream in = channelExec.getInputStream();
            sb = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
            String buf = null;
            while ((buf = reader.readLine()) != null){
                sb.append(buf);
               log.error(buf);
            }
            reader.close();
            log.error("执行命令：{}------------end---------",cmd);

        } finally {
            channelExec.disconnect();
            session.disconnect();
        }
        return sb.toString();
    }


    /**
     *  创建多级目录
     * @param dirPath
     * @param sftp
     * @return
     */
    private boolean createDirs(String dirPath, ChannelSftp sftp) {
        if (dirPath != null && !dirPath.isEmpty()
                && sftp != null) {
            String[] dirs = Arrays.stream(dirPath.split("/"))
                    .filter(StringUtils::isNotBlank)
                    .toArray(String[]::new);

            for (String dir : dirs) {
                try {
                    sftp.cd(dir);
                    log.info("Change directory {}", dir);
                } catch (Exception e) {
                    try {
                        sftp.mkdir(dir);
                        log.info("Create directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Create directory failure, directory:{}", dir, e1);
                        e1.printStackTrace();
                    }
                    try {
                        sftp.cd(dir);
                        log.info("Change directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Change directory failure, directory:{}", dir, e1);
                        e1.printStackTrace();
                    }
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 将文件上传到指定目录
     * @param targetPath
     * @param file
     * @return
     * @throws Exception
     */
    public boolean uploadFile(String targetPath, File file) throws Exception {
        return this.uploadFile(targetPath, new FileInputStream(file),file.getName());
    }

    /**
     * 开启
     * @return
     * @throws Exception
     */
    public ChannelSftp open() throws Exception {
        try {
            ChannelSftp sftp = this.createSftp();
            return sftp;
        } catch (Exception e) {
            log.info("ChannelSftp open failure: {}", e);
            throw new Exception("ChannelSftp open failure");
        }
    }

    /**
     * 删除文件
     * @param targetPath
     * @return
     * @throws Exception
     */
    public boolean deleteFile(String targetPath) throws Exception {
        ChannelSftp sftp = null;
        try {
            log.info("deleteFile path to {}",targetPath);
            sftp = this.createSftp();
            sftp.rm(targetPath);
            return true;
        } catch (Exception e) {
            log.error("Delete file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Delete File failure");
        } finally {
            this.disconnect(sftp);
        }
    }

    public void uploadFile(byte[] content, int len, String targetPath) throws Exception {

        ChannelSftp sftp = this.createSftp();
        OutputStream os = null;
        try {
            log.info("uploadFile path {}",targetPath);
            int index = targetPath.lastIndexOf("/");
            String fileDir = targetPath.substring(0, index);
            String fileName = targetPath.substring(index + 1);
            boolean dirs = this.createDirs(fileDir, sftp);
            if (!dirs) {
                log.error("Remote path error. path:{}", targetPath);
                throw new Exception("Upload File failure");
            }
            os = sftp.put(targetPath);
            os.write(content, 0, len);
        } catch (Exception e) {
            log.error("Upload file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Upload File failure");
        } finally {
            this.disconnect(sftp);
        }
    }

    public void uploadBatch(String targetPath, List<MultipartFile> fileList) throws Exception {
        ChannelSftp sftp = this.createSftp();
        try {
            log.info("uploadFile path {}",targetPath);
            sftp.cd(targetPath);
            for (MultipartFile file : fileList) {
                InputStream inputStream = file.getInputStream();
                try {
                    sftp.put(inputStream, file.getOriginalFilename());
                } catch (SftpException e) {
                    log.error(file.getOriginalFilename() + "上传失败", e);
                }finally {
                    inputStream.close();
                }
            }
        } catch (Exception e) {
            log.error("Upload file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Upload File failure");
        } finally {

            this.disconnect(sftp);
        }
    }
}
